home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / net_src.arc / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-08  |  14.8 KB  |  676 lines

  1. /* FTP client (interactive user) code */
  2. #define    LINELEN        128    /* Length of command buffer */
  3. #include <stdio.h>
  4. #ifdef __TURBOC__
  5. #include "fcntl.h"
  6. #endif
  7. #include "global.h"
  8. #include "mbuf.h"
  9. #include "netuser.h"
  10. #include "icmp.h"
  11. #include "timer.h"
  12. #include "tcp.h"
  13. #include "ftp.h"
  14. #include "session.h"
  15. #include "cmdparse.h"
  16. #include "telnet.h"
  17. #include "iface.h"
  18. #include "ax25.h"
  19. #include "lapb.h"
  20. #include "finger.h"
  21. #include "nr4.h"
  22.  
  23. /* #ifdef __TURBOC__    */
  24. /* #include <fcntl.h>    */
  25. /* #endif        */
  26.  
  27. extern struct session *current;
  28. extern char nospace[];
  29. extern char badhost[];
  30. static char notsess[] = "Not an FTP session!\n";
  31. static char cantwrite[] = "Can't write %s\n";
  32. static char cantread[] = "Can't read %s\n";
  33.  
  34. int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
  35.     domkdir(),dormdir();
  36.  
  37. struct cmds ftpabort[] = {
  38.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  39.     "abort",    doabort,    0,    NULLCHAR,        NULLCHAR,
  40.     NULLCHAR,    NULLFP,        0,    "Only valid command is \"abort\"", NULLCHAR,
  41. };
  42.  
  43. struct cmds ftpcmds[] = {
  44.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  45.     "cd",        doftpcd,    2,    "cd <directory>",    NULLCHAR,
  46.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  47.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  48.     "get",        doget,        2,    "get remotefile <localfile>",    NULLCHAR,
  49.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  50.     "mkdir",    domkdir,    2,    "mkdir <directory>",    NULLCHAR,
  51.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  52.     "rmdir",    dormdir,    2,    "rmdir <directory>",    NULLCHAR,
  53.     "put",        doput,        2,    "put localfile <remotefile>",    NULLCHAR,
  54.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  55.     NULLCHAR,    NULLFP,        0,     NULLCHAR,        NULLCHAR,
  56. };
  57.  
  58. /* Handle top-level FTP command */
  59. doftp(argc,argv)
  60. int argc;
  61. char *argv[];
  62. {
  63.     int32 resolve();
  64.     int ftpparse();
  65.     char *inet_ntoa();
  66.     void ftpccr(),ftpccs();
  67.     struct session *s;
  68.     struct ftp *ftp,*ftp_create();
  69.     struct tcb *tcb;
  70.     struct socket lsocket,fsocket;
  71.  
  72.     lsocket.address = ip_addr;
  73.     lsocket.port = lport++;
  74.     if((fsocket.address = resolve(argv[1])) == 0){
  75.         printf(badhost,argv[1]);
  76.         return 1;
  77.     }
  78.     if(argc < 3)
  79.         fsocket.port = FTP_PORT;
  80.     else
  81.         fsocket.port = atoi(argv[2]);
  82.  
  83.     /* Allocate a session control block */
  84.     if((s = newsession()) == NULLSESSION){
  85.         printf("Too many sessions\n");
  86.         return 1;
  87.     }
  88.     current = s;
  89.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  90.         strcpy(s->name,argv[1]);
  91.     s->type = FTP;
  92.     s->parse = ftpparse;
  93.  
  94.     /* Allocate an FTP control block */
  95.     if((ftp = ftp_create(LINELEN)) == NULLFTP){
  96.         s->type = FREE;
  97.         printf(nospace);
  98.         return 1;
  99.     }
  100.     ftp->state = STARTUP_STATE;
  101.     s->cb.ftp = ftp;    /* Downward link */
  102.     ftp->session = s;    /* Upward link */
  103.  
  104.     /* Now open the control connection */
  105.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  106.         0,ftpccr,NULLVFP,ftpccs,0,(char *)ftp);
  107.     ftp->control = tcb;
  108.     go();
  109.     return 0;
  110. }
  111. /* Parse user FTP commands */
  112. int
  113. ftpparse(line,len)
  114. char *line;
  115. int16 len;
  116. {
  117.     struct mbuf *bp;
  118.  
  119.     switch(current->cb.ftp->state){
  120.     case RECEIVING_STATE:
  121.     case SENDING_STATE:
  122.         /* The only command allowed in data transfer state is ABORT */
  123.         if(cmdparse(ftpabort,line) == -1){
  124.             printf("Transfer in progress; only ABORT is acceptable\n");
  125.         }
  126.         fflush(stdout);
  127.         break;
  128.     case COMMAND_STATE:
  129.         /* Save it now because cmdparse modifies the original */
  130.         bp = qdata(line,len);
  131.  
  132.         if(cmdparse(ftpcmds,line) == -1){
  133.             /* Send it direct */
  134.             if(bp != NULLBUF)
  135.                 send_tcp(current->cb.ftp->control,bp);
  136.             else
  137.                 printf(nospace);
  138.         } else {
  139.             free_p(bp);
  140.         }
  141.         fflush(stdout);
  142.         break;
  143.     case STARTUP_STATE:        /* Starting up autologin */
  144.         printf("Not connected yet, ignoring %s\r\n",line);
  145.         break;
  146.     case USER_STATE:        /* Got the user name */
  147.         line[len] = '\0';
  148.         return sndftpmsg(current->cb.ftp,"USER %s",line);
  149.     case PASS_STATE:        /* Got the password */
  150.         cooked();
  151.         line[len] = '\0';
  152.         return sndftpmsg(current->cb.ftp,"PASS %s",line);
  153.     }
  154. }
  155. /* Handle null line to avoid trapping on first command in table */
  156. static
  157. int
  158. donothing(argc,argv)
  159. int argc;
  160. char *argv[];
  161. {
  162. }
  163. /* Translate 'cd' to 'cwd' for convenience */
  164. static
  165. int
  166. doftpcd(argc,argv)
  167. int argc;
  168. char *argv[];
  169. {
  170.     register struct ftp *ftp;
  171.  
  172.     ftp = current->cb.ftp;
  173.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  174. }
  175. /* Translate 'mkdir' to 'xmkd' for convenience */
  176. static
  177. int
  178. domkdir(argc,argv)
  179. int argc;
  180. char *argv[];
  181. {
  182.     register struct ftp *ftp;
  183.  
  184.     ftp = current->cb.ftp;
  185.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  186. }
  187. /* Translate 'rmdir' to 'xrmd' for convenience */
  188. static
  189. int
  190. dormdir(argc,argv)
  191. int argc;
  192. char *argv[];
  193. {
  194.     register struct ftp *ftp;
  195.  
  196.     ftp = current->cb.ftp;
  197.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  198. }
  199. /* Handle "type" command from user */
  200. static
  201. int
  202. dotype(argc,argv)
  203. int argc;
  204. char *argv[];
  205. {
  206.     register struct ftp *ftp;
  207.  
  208.     ftp = current->cb.ftp;
  209.     if(argc < 2){
  210.         switch(ftp->type){
  211.         case IMAGE_TYPE:
  212.             printf("Image\n");
  213.             break;
  214.         case ASCII_TYPE:
  215.             printf("Ascii\n");
  216.             break;
  217.         }
  218.         return 0;
  219.     }
  220.     switch(*argv[1]){
  221.     case 'i':
  222.     case 'b':
  223.         ftp->type = IMAGE_TYPE;
  224.         sndftpmsg(ftp,"TYPE I\r\n");
  225.         break;
  226.     case 'a':
  227.         ftp->type = ASCII_TYPE;
  228.         sndftpmsg(ftp,"TYPE A\r\n");
  229.         break;
  230.     case 'l':
  231.         ftp->type = IMAGE_TYPE;
  232.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  233.         break;
  234.     default:
  235.         printf("Invalid type %s\n",argv[1]);
  236.         return 1;
  237.     }
  238.     return 0;
  239. }
  240. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  241. static
  242. doget(argc,argv)
  243. int argc;
  244. char *argv[];
  245. {
  246.     void ftpdr(),ftpcds();
  247.     char *remotename,*localname;
  248.     register struct ftp *ftp;
  249.     char *mode;
  250.  
  251.     ftp = current->cb.ftp;
  252.     if(ftp == NULLFTP){
  253.         printf(notsess);
  254.         return 1;
  255.     }
  256.     remotename = argv[1];
  257.     if(argc < 3)
  258.         localname = remotename;
  259.     else
  260.         localname = argv[2];
  261.  
  262.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  263.         fclose(ftp->fp);
  264.     ftp->fp = NULLFILE;
  265.  
  266.     if(ftp->type == IMAGE_TYPE) 
  267.         mode = binmode[WRITE_BINARY];
  268.     else
  269.         mode = "w";
  270.  
  271.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  272.         printf(cantwrite,localname);
  273.         return 1;
  274.     }
  275.     ftp->state = RECEIVING_STATE;
  276.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  277.  
  278.     /* Generate the command to start the transfer */
  279.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  280. }
  281. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  282. static
  283. dolist(argc,argv)
  284. int argc;
  285. char *argv[];
  286. {
  287.     void ftpdr(),ftpcds();
  288.     register struct ftp *ftp;
  289.  
  290.     ftp = current->cb.ftp;
  291.     if(ftp == NULLFTP){
  292.         printf(notsess);
  293.         return 1;
  294.     }
  295.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  296.         fclose(ftp->fp);
  297.     ftp->fp = NULLFILE;
  298.  
  299.     if(argc < 3){
  300.         ftp->fp = stdout;
  301.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  302.         printf(cantwrite,argv[2]);
  303.         return 1;
  304.     }
  305.     ftp->state = RECEIVING_STATE;
  306.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  307.     /* Generate the command to start the transfer
  308.      * It's done this way to avoid confusing the 4.2 FTP server
  309.      * if there's no argument
  310.      */
  311.     if(argc > 1)
  312.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  313.     else
  314.         return sndftpmsg(ftp,"LIST\r\n","");
  315. }
  316. /* Abbreviated (name only) list of remote directory.
  317.  * Syntax: ls <remote directory/file> [<local name>]
  318.  */
  319. static
  320. dols(argc,argv)
  321. int argc;
  322. char *argv[];
  323. {
  324.     void ftpdr(),ftpcds();
  325.     register struct ftp *ftp;
  326.  
  327.     ftp = current->cb.ftp;
  328.     if(ftp == NULLFTP){
  329.         printf(notsess);
  330.         return 1;
  331.     }
  332.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  333.         fclose(ftp->fp);
  334.     ftp->fp = NULLFILE;
  335.  
  336.     if(argc < 3){
  337.         ftp->fp = stdout;
  338.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  339.         printf(cantwrite,argv[2]);
  340.         return 1;
  341.     }
  342.     ftp->state = RECEIVING_STATE;
  343.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  344.     /* Generate the command to start the transfer */
  345.     if(argc > 1)
  346.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  347.     else
  348.         return sndftpmsg(ftp,"NLST\r\n","");
  349. }
  350. /* Start transmit. Syntax: put <local name> [<remote name>] */
  351. static
  352. doput(argc,argv)
  353. int argc;
  354. char *argv[];
  355. {
  356.     void ftpdt(),ftpcds();
  357.     char *remotename,*localname;
  358.     char *mode;
  359.     struct ftp *ftp;
  360.  
  361.     if((ftp = current->cb.ftp) == NULLFTP){
  362.         printf(notsess);
  363.         return 1;
  364.     }
  365.     localname = argv[1];
  366.     if(argc < 3)
  367.         remotename = localname;
  368.     else
  369.         remotename = argv[2];
  370.  
  371.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  372.         fclose(ftp->fp);
  373.  
  374.     if(ftp->type == IMAGE_TYPE) 
  375.         mode = binmode[READ_BINARY];
  376.     else
  377.         mode = "r";
  378.  
  379.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  380.         printf(cantread,localname);
  381.         return 1;
  382.     }
  383.     ftp->state = SENDING_STATE;
  384.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  385.  
  386.     /* Generate the command to start the transfer */
  387.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  388. }
  389. /* Abort a GET or PUT operation in progress. Note: this will leave
  390.  * the partial file on the local or remote system
  391.  */
  392. doabort(argc,argv)
  393. int argc;
  394. char *argv[];
  395. {
  396.     register struct ftp *ftp;
  397.  
  398.     ftp = current->cb.ftp;
  399.  
  400.     /* Close the local file */
  401.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  402.         fclose(ftp->fp);
  403.     ftp->fp = NULLFILE;
  404.  
  405.     switch(ftp->state){
  406.     case SENDING_STATE:
  407.         /* Send a premature EOF.
  408.          * Unfortunately we can't just reset the connection
  409.          * since the remote side might end up waiting forever
  410.          * for us to send something.
  411.          */
  412.         close_tcp(ftp->data);
  413.         printf("Put aborted\n");
  414.         break;
  415.     case RECEIVING_STATE:
  416.         /* Just exterminate the data channel TCB; this will
  417.          * generate a RST on the next data packet which will
  418.          * abort the sender
  419.          */
  420.         del_tcp(ftp->data);
  421.         ftp->data = NULLTCB;
  422.         printf("Get aborted\n");
  423.         break;
  424.     }
  425.     ftp->state = COMMAND_STATE;
  426.     fflush(stdout);
  427. }
  428. /* create data port, and send PORT message */
  429. static
  430. ftpsetup(ftp,recv,send,state)
  431. struct ftp *ftp;
  432. void (*send)();
  433. void (*recv)();
  434. void (*state)();
  435. {
  436.     struct socket lsocket;
  437.     struct mbuf *bp;
  438.  
  439.     lsocket.address = ip_addr;
  440.     lsocket.port = lport++;
  441.  
  442.     /* Compose and send PORT a,a,a,a,p,p message */
  443.  
  444.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  445.         printf(nospace);
  446.         return;
  447.     }
  448.     /* I know, this looks gross, but it works! */
  449.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  450.         hibyte(hiword(lsocket.address)),
  451.         lobyte(hiword(lsocket.address)),
  452.         hibyte(loword(lsocket.address)),
  453.         lobyte(loword(lsocket.address)),
  454.         hibyte(lsocket.port),
  455.         lobyte(lsocket.port));
  456.     bp->cnt = strlen(bp->data);
  457.     send_tcp(ftp->control,bp);
  458.  
  459.     /* Post a listen on the data connection */
  460.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  461.         recv,send,state,0,(char *)ftp);
  462. }
  463. /* FTP Client Control channel Receiver upcall routine */
  464. void
  465. ftpccr(tcb,cnt)
  466. register struct tcb *tcb;
  467. int16 cnt;
  468. {
  469.     struct mbuf *bp;
  470.     struct ftp *ftp;
  471.     void doreply();
  472.     char c;
  473.  
  474.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  475.         /* Unknown connection; kill it */
  476.         close_tcp(tcb);
  477.         return;
  478.     }
  479.     /* Hold output if we're not the current session */
  480.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  481.         return;
  482.  
  483.     if(recv_tcp(tcb,&bp,cnt) > 0){
  484.         while(pullup(&bp,&c,1) == 1){
  485.             switch(c){
  486.             case '\r':    /* Strip cr's */
  487.                 continue;
  488.             case '\n':    /* Complete line; process it */
  489.                 ftp->buf[ftp->cnt] = '\0';
  490.                 doreply(ftp);
  491.                 ftp->cnt = 0;
  492.                 break;
  493.             default:    /* Assemble line */
  494.                 if(ftp->cnt != LINELEN-1)
  495.                     ftp->buf[ftp->cnt++] = c;
  496.                 break;
  497.             }
  498.         }
  499.         fflush(stdout);
  500.     }
  501. }
  502.  
  503.  
  504. /* Process replies from the server */
  505. static
  506. void
  507. doreply(ftp)
  508. register struct ftp *ftp;
  509. {
  510.     static char crlf[]="\n";
  511.  
  512.     fwrite(ftp->buf,1,(unsigned)ftp->cnt,stdout);
  513.     fputc('\n', stdout);
  514.     if (ftp->cnt < 3) return;
  515.     ftp->buf[3] = '\0';
  516.     switch(ftp->state){
  517.     case SENDING_STATE:
  518.     case RECEIVING_STATE:
  519.         if (ftp->buf[0] == '5') doabort();
  520.         break;
  521.     case STARTUP_STATE:
  522.         if (!strcmp(ftp->buf, "220")){
  523.             ftp->state = USER_STATE;
  524.             printf("Enter user name: ");
  525.             fflush(stdout);
  526.         } else ftp->state = COMMAND_STATE;
  527.         break;
  528.     case USER_STATE:
  529.         if (!strcmp(ftp->buf, "331")) {
  530.             ftp->state = PASS_STATE;
  531.             noecho();
  532.             printf("Password: ");
  533.             fflush(stdout);
  534.         } else ftp->state = COMMAND_STATE;
  535.         break;
  536.     case PASS_STATE:
  537.         echo();
  538.         ftp->state = COMMAND_STATE;
  539.         break;
  540.     }
  541. }
  542.  
  543. /* FTP Client Control channel State change upcall routine */
  544. static
  545. void
  546. ftpccs(tcb,old,new)
  547. register struct tcb *tcb;
  548. char old,new;
  549. {
  550.     void ftp_delete();
  551.     struct ftp *ftp;
  552.     char notify = 0;
  553.     extern char *tcpstates[];
  554.     extern char *reasons[];
  555.     extern char *unreach[];
  556.     extern char *exceed[];
  557.  
  558.     /* Can't add a check for unknown connection here, it would loop
  559.      * on a close upcall! We're just careful later on.
  560.      */
  561.     ftp = (struct ftp *)tcb->user;
  562.  
  563.     if(current != NULLSESSION && current->cb.ftp == ftp)
  564.         notify = 1;
  565.  
  566.     switch(new){
  567.     case CLOSE_WAIT:
  568.         if(notify)
  569.             printf("%s\n",tcpstates[new]);
  570.         close_tcp(tcb);
  571.         break;
  572.     case CLOSED:    /* heh heh */
  573.         if(notify){
  574.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  575.             if(tcb->reason == NETWORK){
  576.                 switch(tcb->type){
  577.                 case DEST_UNREACH:
  578.                     printf(": %s unreachable",unreach[tcb->code]);
  579.                     break;
  580.                 case TIME_EXCEED:
  581.                     printf(": %s time exceeded",exceed[tcb->code]);
  582.                     break;
  583.                 }
  584.             }
  585.             printf(")\n");
  586.             cmdmode();
  587.         }
  588.         del_tcp(tcb);
  589.         if(ftp != NULLFTP)
  590.             ftp_delete(ftp);
  591.         break;
  592.     default:
  593.         if(notify)
  594.             printf("%s\n",tcpstates[new]);
  595.         break;
  596.     }
  597.     if(notify)
  598.         fflush(stdout);
  599. }
  600. /* FTP Client Data channel State change upcall handler */
  601. static
  602. void
  603. ftpcds(tcb,old,new)
  604. struct tcb *tcb;
  605. char old,new;
  606. {
  607.     struct ftp *ftp;
  608.  
  609.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  610.         /* Unknown connection, kill it */
  611.         close_tcp(tcb);
  612.         return;
  613.     }
  614.     switch(new){
  615.     case FINWAIT2:
  616.     case TIME_WAIT:
  617.         if(ftp->state == SENDING_STATE){
  618.             /* We've received an ack of our FIN, so
  619.              * return to command mode
  620.              */
  621.             ftp->state = COMMAND_STATE;
  622.             if(current != NULLSESSION && current->cb.ftp == ftp){
  623.                 printf("Put complete, %lu bytes sent\n",
  624.                     tcb->snd.una - tcb->iss - 2);
  625.                 fflush(stdout);
  626.             }
  627.         }
  628.         break;        
  629.     case CLOSE_WAIT:
  630.         close_tcp(tcb);
  631.         if(ftp->state == RECEIVING_STATE){
  632.             /* End of file received on incoming file */
  633. #ifdef    CPM
  634.             if(ftp->type == ASCII_TYPE)
  635.                 putc(CTLZ,ftp->fp);
  636. #endif
  637.             if(ftp->fp != stdout)
  638.                 fclose(ftp->fp);
  639.             ftp->fp = NULLFILE;
  640.             ftp->state = COMMAND_STATE;
  641.             if(current != NULLSESSION && current->cb.ftp == ftp){
  642.                 printf("Get complete, %lu bytes received\n",
  643.                     tcb->rcv.nxt - tcb->irs - 2);
  644.                 fflush(stdout);
  645.             }
  646.         }
  647.         break;
  648.     case CLOSED:
  649.         ftp->data = NULLTCB;
  650.         del_tcp(tcb);
  651.         break;
  652.     }
  653. }
  654. /* Send a message on the control channel */
  655. /*VARARGS*/
  656. static
  657. int
  658. sndftpmsg(ftp,fmt,arg)
  659. struct ftp *ftp;
  660. char *fmt;
  661. char *arg;
  662. {
  663.     struct mbuf *bp;
  664.     int16 len;
  665.  
  666.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  667.     if((bp = alloc_mbuf(len)) == NULLBUF){
  668.         printf(nospace);
  669.         return 1;
  670.     }
  671.     sprintf(bp->data,fmt,arg);
  672.     bp->cnt = strlen(bp->data);
  673.     send_tcp(ftp->control,bp);
  674.     return 0;
  675. }
  676.